package com.jic.member.impl;


import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.jic.common.base.tuple.Tuple;
import com.jic.common.base.vo.enums.DeptTypeEnums;
import com.jic.member.constant.enums.*;
import com.jic.member.entity.*;
import com.jic.member.entity.model.*;
import com.jic.member.mapper.*;
import com.jic.member.service.EventListenerService;
import com.jic.member.service.TripTimingJobService;
import com.jic.member.service.eventHandler.EventHander;
import com.jic.member.service.eventHandler.EventHandlerFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.function.Predicate;

/**
 * description: EventListenerServiceImpl
 * date: 2020/6/8 13:46
 * author: wangys
 * version: 1.0
 */
@Service
@Slf4j
public class EventListenerServiceImpl implements EventListenerService {

    @Autowired
    EventHandlerFactory eventHandlerFactory;

    @Autowired
    TripBranchNodeEntityMapper tripBranchNodeEntityMapper;

    @Autowired
    TripEntityMapper tripEntityMapper;

    @Autowired
    TripBranchEntityMapper tripBranchEntityMapper;

    @Autowired
    TripExecuteInfoEntityMapper tripExecuteInfoEntityMapper;

    @Autowired
    TripEventInfoEntityMapper tripEventInfoEntityMapper;

    @Autowired
    TripPeopleEntityMapper tripPeopleEntityMapper;

    @Resource
    private TripTimingJobService tripTimingJobService;

    private static final Predicate<EventListenerContent> COMMON_PARAM_CHECK = (t) -> {
        return StringUtils.isNotBlank(t.getThirdUserId()) && StringUtils.isNotBlank(t.getEventCode()) && t.getEventType() != null && t.getEventTime() != null;
    };

    //
    private static final Predicate<TripEntity> ING_CKECK = (t) -> {
        long currentTime = System.currentTimeMillis();
        return t.getStatus().equals(TripStatusEnums.ING.getCode());
    };

    private static final String START_NODE = "start_node";

    private static final String TIMED_TRIGGER_NODE = "timed_trigger_node";

    private static final String WAIT_NODE = "wait_node";

    private static final List<Integer> CAN_PROCESS_CONDITION = Lists.newArrayList(0, 1);
    /**
     * 1. 根据事件类型，事件code 查询出所有符合的节点分支
     * 2. 筛选出进行中的旅程
     * 3. 校验事件的本身参数（事件时间有效期，时间条件等）
     * 4. 根据用户判断是否可以执行此节点（父节点是否已经执行，本节点是否已经执行）
     * 5.存入trip_people表（事件选择需要存入）
     * 6.存入trip_event_info表 trip_execute_info 表
     *
     * @param content
     */
    @Override
    @Transactional
    public void processEvent(EventListenerContent content) {

        final String eventCode = content.getEventCode();
        final Integer eventType = content.getEventType();
        final String thirdUserId = content.getThirdUserId();
        final JSONObject param = content.getParam();
        final Integer deptType = content.getDeptType();
        final List<Long> deptIds = content.getDeptIds();
        log.info("事件监听,processEvent,userId = {}, eventCode = {}, param = {}", thirdUserId, eventCode, param);

        final EventHander eventHander = eventHandlerFactory.getEventHanderByEventCode(eventCode);
        if(eventHander == null){
            log.info("事件监听,没有匹配的处理器,userId = {}, eventCode = {}, param = {}", thirdUserId, eventCode, param);
            return;
        }
        //必传参数校验
        if(COMMON_PARAM_CHECK.test(content) && eventHander.preValidateParam().test(param)){
            log.info("事件监听,入参校验失败,userId = {}, eventCode = {}, param = {}", thirdUserId, eventCode, param);
            return;
        }

        //1. 根据事件类型，事件code 查询出所有符合的节点分支
        TripBranchNodeEntity branchNodeEntity = new TripBranchNodeEntity();
        branchNodeEntity.setEventCode(eventCode);
        branchNodeEntity.setEventType(eventType);
        branchNodeEntity.setDeleteFlag(DeleteFlagEnums.UN_DELETE.getCode());
        List<TripBranchNodeEntity> branchNodeEntities = tripBranchNodeEntityMapper.findAll(branchNodeEntity);

        if (CollectionUtils.isEmpty(branchNodeEntities)) {
            log.info("事件监听,没有命中符合的旅程,userId = {}, eventCode = {}, param = {}", thirdUserId, eventCode, param);
            return;
        }

        branchNodeEntities
                .stream()
                .map(b->{
                    TripBranchNode tripBranchNode = convertToTripBranchNode(b);
                    content.getTripBranchNodeMap().put(tripBranchNode.getId(), tripBranchNode);
                    TripBranchNodeEntity preTripBranchNodeEntity = tripBranchNodeEntityMapper.queryById(b.getPrevBranchNodeId());
                    tripBranchNode.setPrevBranchTemplateCode(preTripBranchNodeEntity != null ? preTripBranchNodeEntity.getNodeTemplateCode() : null);
                    return tripBranchNode;
                })
                .filter(b->{
                    //过滤掉没有连线的脏数据
                    return b.getTripId() != null;
                }).filter(b -> {
                    boolean deptFlag = true;
                    // 2筛选出进行中的旅程 && 所属组织的旅程，deptId TODO 根据本平台查询上级平台创建或者本平台自己创建的或者下级创建的
                    //TripBranchEntity branchEntity = tripBranchEntityMapper.queryById(b.getBranchId());
                    TripEntity tripEntity = tripEntityMapper.queryById(b.getTripId());
                    b.setTripId(tripEntity.getId());
                    if(deptType != null && CollectionUtils.isNotEmpty(deptIds)){
                        if(DeptTypeEnums.CHILD_PLATFORM.getCode().equals(deptType)){//子平台
                            if(!tripEntity.getDeptType().equals(DeptTypeEnums.OPERATION_PLATFORM.getCode()) && !deptIds.contains(tripEntity.getDeptId())){
                                deptFlag = false;
                            }
                        }
                    }
                    return deptFlag && ING_CKECK.test(tripEntity);
                }).filter(b->{
                    if(StringUtils.isEmpty(b.getPrevBranchTemplateCode())){
                        log.info("事件监听,上级节点为空,nodeId = {},userId = {}, eventCode = {}, param = {}, nodeId = {}", b.getId(), thirdUserId, eventCode, b.getId());
                        return false;
                    }
                    if(StringUtils.isEmpty(b.getNodeVariable())){
                        log.info("事件监听,参数设置为空,nodeId = {},userId = {}, eventCode = {}, param = {}, nodeId = {}", b.getId(), thirdUserId, eventCode, b.getId());
                        return false;
                    }
                    // 3校验事件的本身参数（事件时间有效期，时间条件等)
                    Tuple<Boolean, String> tuple = eventHander.validateParam(content, b.getId());
                    log.info("事件监听,参数匹配校验,nodeId = {}, userId = {}, eventCode = {}, param = {}, result = {}", b.getId(), thirdUserId, eventCode, param, JSONObject.toJSONString(tuple));
                    return tuple.getF();
                }).filter(b->{
                    //4. 根据用户判断是否可以执行此节点（父节点是否已经执行，本节点是否已经执行）
                    Boolean preFlag = true;
                    if(b.getPrevBranchNodeId() != null && !b.getPrevBranchTemplateCode().equals(START_NODE) && !checkPrevBranch(b.getPrevBranchNodeId(), thirdUserId)){
                        preFlag = false;
                    }
                    Boolean currentFlag = !checkPrevBranch(b.getId(), thirdUserId);
                    log.info("事件监听, 触发下一步执行开始,nodeId = {}, eventCode = {}, userId = {},preFlag = {}, currentFlag = {} ", b.getId(), eventCode, thirdUserId, preFlag, currentFlag);
                    return preFlag && currentFlag;
                }).forEach(b->{
                    // 5.存入trip_people表（事件选择需要存入）
                    if(b.getPrevBranchTemplateCode().equals(START_NODE)){
                        TripPeopleEntity tripPeopleEntity = new TripPeopleEntity();
                        tripPeopleEntity.setThirdUserId(thirdUserId);
                        tripPeopleEntity.setBranchId(b.getBranchId());
                        tripPeopleEntity.setTripId(b.getTripId());
                        tripPeopleEntity.setBranchNodeId(b.getId());
                        tripPeopleEntity.setNodeTemplateCode(b.getNodeTemplateCode());
                        tripPeopleEntityMapper.insert(tripPeopleEntity);
                    }
                    // 6.存入trip_event_info表 trip_execute_info 表
                    TripEventInfoEntity tripEventInfoEntity = new TripEventInfoEntity();
                    tripEventInfoEntity.setThirdUserId(thirdUserId);
                    tripEventInfoEntity.setBranchId(b.getBranchId());
                    tripEventInfoEntity.setTripId(b.getTripId());
                    tripEventInfoEntity.setBranchNodeId(b.getId());
                    tripEventInfoEntity.setNodeTemplateCode(b.getNodeTemplateCode());
                    tripEventInfoEntity.setRequestVariable(param.toJSONString());
                    tripEventInfoEntityMapper.insert(tripEventInfoEntity);

                    TripExecuteInfoEntity tripExecuteInfoEntity = new TripExecuteInfoEntity();
                    tripExecuteInfoEntity.setThirdUserId(thirdUserId);
                    tripExecuteInfoEntity.setBranchId(b.getBranchId());
                    tripExecuteInfoEntity.setTripId(b.getTripId());
                    tripExecuteInfoEntity.setBranchNodeId(b.getId());
                    tripExecuteInfoEntity.setNodeTemplateCode(b.getNodeTemplateCode());
                    tripExecuteInfoEntity.setRequestVariable(null);
                    tripExecuteInfoEntity.setStatus(1);
                    tripExecuteInfoEntity.setType(2);
                    tripExecuteInfoEntityMapper.insert(tripExecuteInfoEntity);

                    processNextNode(b.getId(), content);
                });
    }

    /**
     * 处理下一个节点，事件选择，条件下只能
     * @param id
     */
    private void processNextNode(Long id, EventListenerContent content) {
        TripBranchNodeEntity param = new TripBranchNodeEntity();
        param.setDeleteFlag(DeleteFlagEnums.UN_DELETE.getCode());
        param.setPrevBranchNodeId(id);
        tripBranchNodeEntityMapper.findAll(param)
                .stream()
                .filter(t->{
                    return !t.getNodeTemplateCode().equals("event_judge_node") && CAN_PROCESS_CONDITION.contains(t.getBranchCondition()) ;
                }).forEach(t->{
            log.info("事件监听, 触发下一步执行开始, tripId = {},nodeId = {}, userId = {}", t.getTripId(), t.getId(), content.getThirdUserId());
//            JSONObject nodeVariable = JSONObject.parseObject(t.getNodeVariable());
//            Date exeDate = null;
//            //定时
//            if(t.getNodeTemplateCode().equals(WAIT_NODE) && nodeVariable.getInteger("waitType") == 2){
//                LocalDateTime localDateTime = LocalDateTime.now();
//                LocalDateTime nextLocalDateTime = null;
//                Integer waitPeriod = nodeVariable.getInteger("waitPeriod");//WaitePeriodEnum
//                Integer waitTime = nodeVariable.getInteger("waitTime");
//                switch (waitPeriod){
//                    case 1:
//                        nextLocalDateTime = localDateTime.plusMinutes(waitTime);
//                        break;
//                    case 2:
//                        nextLocalDateTime = localDateTime.plusHours(waitTime);
//                        break;
//                    case 3:
//                        nextLocalDateTime = localDateTime.plusDays(waitTime);
//                        break;
//                    case 4:
//                        nextLocalDateTime = localDateTime.plusWeeks(waitTime);
//                        break;
//                    case 5:
//                        nextLocalDateTime = localDateTime.plusMonths(waitTime);
//                        break;
//                    default:
//                        nextLocalDateTime = null;
//                }
//                exeDate = nextLocalDateTime != null ? Date.from(Instant.from(nextLocalDateTime.atZone(ZoneId.systemDefault()))) : null;
//            }


            Date date = new Date() ;
            if(t.getNodeTemplateCode().equals(NodeTemplateTypeEnum.WAIT_NODE.getCode())){
                String nodeVariable = t.getNodeVariable();
                // 解析json
                JSONObject jsonObject = JSONObject.parseObject(nodeVariable);
                Integer waitType = (Integer) jsonObject.get("waitType");
                if(waitType.equals(1)){//直接触发
                    date = new Date();
                }else {
                    String waitTimeStr = (String) jsonObject.get("waitTime");
                    Integer waitTime = Integer.valueOf(waitTimeStr);
                    Integer waitPeriod = (Integer) jsonObject.get("waitPeriod");
                    if(WaitePeriodEnum.MINUTE.getCode().equals(waitPeriod)){
                        date = new Date(date.getTime() + waitTime * waitPeriod * 60 * 1000);
                    }
                    else if(WaitePeriodEnum.HOUR.getCode().equals(waitPeriod)){
                        date = new Date(date.getTime() + waitTime * waitPeriod * 60 * 60 * 1000);
                    }
                    else if(WaitePeriodEnum.DAY.getCode().equals(waitPeriod)){
                        date = new Date(date.getTime() + waitTime * waitPeriod * 24 * 60 * 60 * 1000);
                    }
                    else if(WaitePeriodEnum.WEEK.getCode().equals(waitPeriod)){
                        date = new Date(date.getTime() + waitTime * waitPeriod * 7 * 24 * 60 * 60 * 1000);
                    }
                    else if(WaitePeriodEnum.MONTH.getCode().equals(waitPeriod)){
                        date = new Date(date.getTime() + waitTime * waitPeriod * 30 * 7 * 24 * 60 * 60 * 1000);
                    }
                }
            }

            tripTimingJobService.register(TripTimingJobTypeEnum.TIMED_TRIGGER, t.getNodeTemplateCode(), t.getId(), date, "事件监听成功触发下一个节点", t.getTripId(), t.getId(), Lists.newArrayList(content.getThirdUserId()));
            log.info("事件监听, 触发下一步执行成功, tripId = {},nodeId = {}, userId = {}", t.getTripId(), t.getId(), content.getThirdUserId());
        });
        // TODO 待确定时间 or 执行下一步 等待 定时触发 发放优惠券、发放红包、推送促销活动、推送商品信息、推送图文、发短信、发微信消息、发邮件、发Apppush消息
        //tripTimingJobService.register(TripTimingJobTypeEnum.TIMED_TRIGGER, tripBranchNodeEntity.getNodeTemplateCode(), tripBranchNodeEntity.getId(), null, "事件监听成功触发下一个节点", content);
    }

    /**
     * 组装原始数据
     * @param tripBranchNodeEntity
     * @return
     */
    private TripBranchNode convertToTripBranchNode(TripBranchNodeEntity tripBranchNodeEntity) {
        return new TripBranchNode()
                .setBranchCondition(tripBranchNodeEntity.getBranchCondition())
                .setBranchId(tripBranchNodeEntity.getBranchId())
                .setEventCode(tripBranchNodeEntity.getEventCode())
                .setEventType(tripBranchNodeEntity.getEventType())
                .setId(tripBranchNodeEntity.getId())
                .setNodeCode(tripBranchNodeEntity.getNodeCode())
                .setNodeTemplateId(tripBranchNodeEntity.getNodeTemplateId())
                .setNodeTemplateCode(tripBranchNodeEntity.getNodeTemplateCode())
                .setNodeVariable(tripBranchNodeEntity.getNodeVariable())
                .setPrevBranchNodeId(tripBranchNodeEntity.getPrevBranchNodeId())
                ;
    }

    /**
     * 校验节点是否执行
     *
     * @param branchNodeId
     * @param userId
     * @return
     */
    private Boolean checkPrevBranch(Long branchNodeId, String userId) {
        boolean bool = false;
        TripExecuteInfoEntity param = new TripExecuteInfoEntity();
        param.setBranchNodeId(branchNodeId);
        param.setThirdUserId(userId);
        param.setStatus(1);
        param.setDeleteFlag(DeleteFlagEnums.UN_DELETE.getCode());
        int count = tripExecuteInfoEntityMapper.countOfPage(param);
        if (count > 0) {
            bool = true;
        }
        return bool;
    }
}
